#include "LumineBuffer.hpp"
#include "Core/Context/LumineContext.hpp"
#include "Utils/LumineUtils.hpp"

#include "Constants.hpp"
namespace Lumine
{
    void LumineBuffer::createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::raii::Buffer &buffer, vk::raii::DeviceMemory &bufferMemory)
    {
        vk::BufferCreateInfo bufferInfo;
        bufferInfo.setSize(size).setUsage(usage).setSharingMode(vk::SharingMode::eExclusive);
        buffer = vk::raii::Buffer(LumineContext::Get().Device(), bufferInfo);
        vk::MemoryRequirements memReq = buffer.getMemoryRequirements();
        vk::MemoryAllocateInfo allocInfo;

        allocInfo.setAllocationSize(memReq.size)
            .setMemoryTypeIndex(findMemoryType(memReq.memoryTypeBits, properties));
        bufferMemory = vk::raii::DeviceMemory(LumineContext::Get().Device(), allocInfo);
        buffer.bindMemory(*bufferMemory, 0);
    }

    uint32_t LumineBuffer::findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags props)
    {
        vk::PhysicalDeviceMemoryProperties memProps = LumineContext::Get().PhysicalDevice().getMemoryProperties();
        for (uint32_t i = 0; i < memProps.memoryTypeCount; i++)
        {
            if ((typeFilter & (1 << i)) && (memProps.memoryTypes[i].propertyFlags & props) == props)
            {
                return i;
            }
        }
        throw std::runtime_error("failed to find suitable memory type!");
    }

    void LumineBuffer::copyBuffer(const vk::raii::Buffer &src, const vk::raii::Buffer &dst, vk::DeviceSize size)
    {
        Utils::execSingleTimeCmds([&](const vk::raii::CommandBuffer &cmdBuffer)
                                  {
        // 记录传输命令
        vk::BufferCopy copyRegion(0, 0, size);
        cmdBuffer.copyBuffer(*src, *dst, copyRegion); });
    }

    void LumineBuffer::createTextureImageView(vk::raii::ImageView &outImageView,vk::ImageAspectFlags aspect, const vk::raii::Image &image, vk::Format format)
    {
        // 图像子资源范围定义
        vk::ImageSubresourceRange subresource{};
        subresource.setAspectMask(aspect)
            .setBaseMipLevel(0)
            .setLevelCount(1)
            .setBaseArrayLayer(0)
            .setLayerCount(1);
        // 图像视图创建信息
        vk::ImageViewCreateInfo viewInfo{};
        viewInfo.setImage(*image)
            .setViewType(vk::ImageViewType::e2D)
            .setFormat(format)
            .setSubresourceRange(subresource);
        // 创建ImageView
        outImageView = vk::raii::ImageView(LumineContext::Get().Device(), viewInfo);
    }

    void LumineBuffer::transitionImageLayout(const vk::raii::Image &image, vk::Format format, vk::ImageLayout oldLayout, vk::ImageLayout newLayout)
    {
        Utils::execSingleTimeCmds([&](const vk::raii::CommandBuffer &cmdBuffer)
                                  {
                                      vk::ImageMemoryBarrier barrier{};
                                      barrier.setOldLayout(oldLayout)
                                          .setNewLayout(newLayout)
                                          .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
                                          .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
                                          .setImage(*image)
                                          .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});

                                      // 同步参数配置
                                      vk::PipelineStageFlagBits sourceStage;
                                      vk::PipelineStageFlagBits dstStage;

                                      // 转换1: Undefined->TransferDstOptimal (为数据拷贝做准备)
                                      if(oldLayout==vk::ImageLayout::eUndefined && newLayout==vk::ImageLayout::eTransferDstOptimal){
                                        barrier.setSrcAccessMask({});
                                        barrier.setDstAccessMask(vk::AccessFlagBits::eTransferWrite);
                                        sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
                                        dstStage = vk::PipelineStageFlagBits::eTransfer;
                                      }
                                      // 转换2: TransferDstOptimal->ShaderReadOnlyOptimal (为着色器采样做准备)
                                      else if(oldLayout==vk::ImageLayout::eTransferDstOptimal && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal){
                                        barrier.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
                                            .setDstAccessMask(vk::AccessFlagBits::eShaderRead);
                                        sourceStage = vk::PipelineStageFlagBits::eTransfer;
                                        dstStage = vk::PipelineStageFlagBits::eFragmentShader;
                                      }else{
                                        throw std::invalid_argument("unsupported layout transition!");
                                      }
                                      cmdBuffer.pipelineBarrier(sourceStage,dstStage,{},{},nullptr,barrier); });
    }

    void LumineBuffer::createTextureSampler(vk::raii::Sampler &sampler)
    {
        vk::PhysicalDeviceProperties properties = LumineContext::Get().PhysicalDevice().getProperties();
        vk::SamplerCreateInfo samplerInfo;
        samplerInfo.setMagFilter(vk::Filter::eLinear)
            .setMinFilter(vk::Filter::eLinear)
            .setMipmapMode(vk::SamplerMipmapMode::eLinear)
            .setAddressModeU(vk::SamplerAddressMode::eRepeat)
            .setAddressModeV(vk::SamplerAddressMode::eRepeat)
            .setAddressModeW(vk::SamplerAddressMode::eRepeat)
            .setAnisotropyEnable(vk::True)
            .setMaxAnisotropy(properties.limits.maxSamplerAnisotropy)
            .setCompareEnable(vk::False)
            .setCompareOp(vk::CompareOp::eAlways)
            .setBorderColor(vk::BorderColor::eIntOpaqueBlack)
            .setUnnormalizedCoordinates(vk::False)
            .setMipmapMode(vk::SamplerMipmapMode::eLinear)
            .setMipLodBias(0.0f)
            .setMinLod(0.0f)
            .setMaxLod(0.0f);
        sampler = vk::raii::Sampler(LumineContext::Get().Device(),samplerInfo); 
    }
}